/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package project2;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Scanner;


/**
 *
 * @author rr
 */
public class SetUp {
    
    private static SetUp instance;
    private SequencesConstraint seqConstraint;
    private BlockLengthConstraint blockConstraint;
    private int stopLimit;
    private boolean decreaseTEveryTime;
    
    private SetUp(){

    }
    
    public static SetUp getInstance(){
        if(instance == null){
            instance = new SetUp();
        }
        return instance;
    }
    
    
    
    private String[] names; 
    private int numberOfEmployees;
    private int lengthOfSchedule;
    private int numberOfShifts;
    private int[][] temporalRequirementsMatrix;
    
    private int kOpt = 2;
    private float stochasticHillClimbingT;
    private float startStochasticHillClimbingT;
    private float simulatedAnnealingR;
    
    private int[] minLengthOfBlocks;
    private int[] maxLengthOfBlocks;
    
    private int minLengthOfWorkBlocks;
    private int maxLengthOfWorkBlocks;
    private int minLengthOfDayOffBlocks;
    private int maxLengthOfDayOffBlocks;
    
    private int nrOfNotAllowedSequences2;
    private int nrOfNotAllowedSequences3;
    
    
    private int[][] notAllowedSequences2;
    private int[][] notAllowedSequences3;
    
    
    public void init(File problemFile) throws FileNotFoundException{
        
        //TODO parse file and set problem params
        
        Scanner input = new Scanner(problemFile);

        while(input.hasNext()) {
            //or to process line by line
            String nextLine = input.nextLine();
            
            //#Length of the schedule
            if(nextLine.contains("#Length of the schedule")){
                    nextLine = input.nextLine();
                    //assign variable
                    Integer dataInt = Integer.parseInt(nextLine);
                    this.lengthOfSchedule = dataInt;                    
                
                
            } //#Number of Employees
            if(nextLine.contains("#Number of Employees")){
                    nextLine = input.nextLine();
                    //assign variable
                    Integer dataInt = Integer.parseInt(nextLine);
                    this.numberOfEmployees = dataInt; 
            } //#Number of Shifts
            if(nextLine.contains("#Number of Shifts")){
                nextLine = input.nextLine();
                    //assign variable
                    Integer dataInt = Integer.parseInt(nextLine);
                    this.numberOfShifts = dataInt; 
            } //# Temporal Requirements Matrix
            if(nextLine.contains("# Temporal Requirements Matrix")){
                
                this.temporalRequirementsMatrix = new int[this.numberOfShifts][this.lengthOfSchedule];
                
                for(int i = 0; i< this.numberOfShifts; ++i){
                    
                    nextLine = input.nextLine();
                    
                    String[] strings = nextLine.split(" ");
                    
                    int offset = 0;
                    for(int j = 0; j<strings.length; ++j){
                        if(strings[j].equals("")){
                            ++offset;
                            continue;
                        }
                        this.temporalRequirementsMatrix[i][j-offset] = Integer.parseInt(strings[j]);
                    }
                }
                
            } //#ShiftName, Start, Length, Name, MinlengthOfBlocks, MaxLengthOfBlocks
            if(nextLine.contains("#ShiftName, Start, Length, Name, MinlengthOfBlocks, MaxLengthOfBlocks")){
                this.names = new String[this.numberOfShifts +1];
                this.maxLengthOfBlocks = new int[this.numberOfShifts];
                this.minLengthOfBlocks = new int[this.numberOfShifts];
                
                this.names[this.numberOfShifts] = "-";
                
                for(int i = 0; i< this.numberOfShifts; ++i){
                    
                    nextLine = input.nextLine();
                    
                    String[] strings = nextLine.split(" ");
                    
                    //should contain ShiftName, Start, Length, Name, MinlengthOfBlocks, MaxLengthOfBlocks
                    
                    this.names[i] = strings[0];
                    this.minLengthOfBlocks[i] = Integer.parseInt(strings[4]);
                    this.maxLengthOfBlocks[i] = Integer.parseInt(strings[5]);
                    
                }
            }//# Minimum and maximum length of days-off blocks 
            if(nextLine.contains("# Minimum and maximum length of days-off blocks")){
                
                nextLine = input.nextLine();
                
                String[] strings = nextLine.split(" ");
                
                this.minLengthOfDayOffBlocks = Integer.parseInt(strings[0]);
                this.maxLengthOfDayOffBlocks = Integer.parseInt(strings[1]);
                
                
                
            }//# Minimum and maximum length of work blocks
            if(nextLine.contains("# Minimum and maximum length of work blocks")){
                
                nextLine = input.nextLine();
                
                String[] strings = nextLine.split(" ");
                
                this.minLengthOfWorkBlocks = Integer.parseInt(strings[0]);
                this.maxLengthOfWorkBlocks = Integer.parseInt(strings[1]);
                
            }//# Number of not allowed shift sequences
            if(nextLine.contains("# Number of not allowed shift sequences")){
                
                nextLine = input.nextLine();
                
                String[] strings = nextLine.split(" ");
                
                this.nrOfNotAllowedSequences2 = Integer.parseInt(strings[0]);
                this.nrOfNotAllowedSequences3 = Integer.parseInt(strings[1]);
            }//# Number of not allowed shift sequences
            if(nextLine.contains("# Not allowed shift sequences ")){
               
                // - means Day Off
                int nrOfLines = this.nrOfNotAllowedSequences2 + this.nrOfNotAllowedSequences3;
                this.notAllowedSequences2 = new int[this.nrOfNotAllowedSequences2][2];
                this.notAllowedSequences3 = new int[this.nrOfNotAllowedSequences3][3];
                
                int twoCount = 0;
                int threeCount = 0;
                for(int i =0; i<nrOfLines; ++i){
                    
                    nextLine = input.nextLine();
                    
                    String[] strings = nextLine.split(" ");
                    if(strings.length == 2){
                        //we have a 2 shift constraint
                        
                        this.notAllowedSequences2[twoCount][0] = Arrays.asList(this.names).indexOf(strings[0]);
                        this.notAllowedSequences2[twoCount][1] = Arrays.asList(this.names).indexOf(strings[1]);
                        ++twoCount;
                    } else if (strings.length == 3){
                        //we have a 3 shift constraint
                        this.notAllowedSequences3[threeCount][0] = Arrays.asList(this.names).indexOf(strings[0]);
                        this.notAllowedSequences3[threeCount][1] = Arrays.asList(this.names).indexOf(strings[1]);
                        this.notAllowedSequences3[threeCount][2] = Arrays.asList(this.names).indexOf(strings[2]);
                        
                        ++threeCount;
                    }
                    
                    
                }
                
            }
                
            
        }

        input.close();
        
        this.seqConstraint=new SequencesConstraint();
        this.blockConstraint=new BlockLengthConstraint();
        this.stochasticHillClimbingT = 500.f/(this.numberOfEmployees * this.lengthOfSchedule);
        
    }
    
    public void testInit(){
        int[][] tempReq = {
                {1,0,1,2},
                {1,2,1,0},
                {0,0,0,0}
        };
        this.temporalRequirementsMatrix = tempReq;
        this.lengthOfSchedule = 4;
        this.numberOfShifts = 4;
        this.numberOfEmployees = 6;
        this.names = new String[4];
        names[0] = "D";
        names[1] = "A";
        names[2] = "N";
        names[3] = "-";
        
        
        this.maxLengthOfDayOffBlocks=6;
        this.maxLengthOfWorkBlocks=12000;
        this.maxLengthOfBlocks=new int[3];
        this.maxLengthOfBlocks[0]=2;
        this.maxLengthOfBlocks[1]=3;
        this.maxLengthOfBlocks[2]=2;
        
        this.minLengthOfDayOffBlocks=0;
        this.minLengthOfWorkBlocks=0;
        this.minLengthOfBlocks=new int[3];
        this.minLengthOfBlocks[0]=0;
        this.minLengthOfBlocks[1]=2;
        this.minLengthOfBlocks[2]=0;
                
        this.blockConstraint=new BlockLengthConstraint();
        
    }
    
    public void testInit2(){
        int[][] tempReq = {
                {0,0,1,2},
                {1,2,1,0},
                {0,0,0,0}
        };
        this.temporalRequirementsMatrix = tempReq;
        this.lengthOfSchedule = 4;
        this.numberOfShifts = 3;
        this.numberOfEmployees = 3;
        this.names = new String[4];
        names[0] = "D";
        names[1] = "A";
        names[2] = "N";
        names[3] = "-";
        
        
        this.maxLengthOfDayOffBlocks=6;
        this.maxLengthOfWorkBlocks=1200;
        this.maxLengthOfBlocks=new int[3];
        this.maxLengthOfBlocks[0]=2;
        this.maxLengthOfBlocks[1]=3;
        this.maxLengthOfBlocks[2]=2;
        
        this.minLengthOfDayOffBlocks=0;
        this.minLengthOfWorkBlocks=0;
        this.minLengthOfBlocks=new int[3];
        this.minLengthOfBlocks[0]=0;
        this.minLengthOfBlocks[1]=2;
        this.minLengthOfBlocks[2]=0;
                
        this.blockConstraint=new BlockLengthConstraint();
        
    }


    public String[] getNames() {
        return names;
    }

    public void setNames(String[] names) {
        this.names = names;
    }

    public int getNumberOfEmployees() {
        return numberOfEmployees;
    }

    public void setNumberOfEmployees(int numberOfEmployees) {
        this.numberOfEmployees = numberOfEmployees;
    }

    public int getNumberOfShifts() {
        return numberOfShifts;
    }

    public void setNumberOfShifts(int numberOfShifts) {
        this.numberOfShifts = numberOfShifts;
    }

    public int[][] getTemporalRequirementsMatrix() {
        return temporalRequirementsMatrix;
    }

    public void setTemporalRequirementsMatrix(int[][] temporalRequirementsMatrix) {
        this.temporalRequirementsMatrix = temporalRequirementsMatrix;
    }

    public int getLengthOfSchedule() {
        return lengthOfSchedule;
    }

    public void setLengthOfSchedule(int lengthOfSchedule) {
        this.lengthOfSchedule = lengthOfSchedule;
    }

    public int[] getMinLengthOfBlocks() {
        return minLengthOfBlocks;
    }

    public void setMinLengthOfBlocks(int[] minLengthOfBlocks) {
        this.minLengthOfBlocks = minLengthOfBlocks;
    }

    public int[] getMaxLengthOfBlocks() {
        return maxLengthOfBlocks;
    }

    public void setMaxLengthOfBlocks(int[] maxLengthOfBlocks) {
        this.maxLengthOfBlocks = maxLengthOfBlocks;
    }


    public int getMinLengthOfWorkBlocks() {
        return minLengthOfWorkBlocks;
    }

    public void setMinLengthOfWorkBlocks(int minLengthOfWorkBlocks) {
        this.minLengthOfWorkBlocks = minLengthOfWorkBlocks;
    }

    public int getMaxLengthOfWorkBlocks() {
        return maxLengthOfWorkBlocks;
    }

    public void setMaxLengthOfWorkBlocks(int maxLengthOfWorkBlocks) {
        this.maxLengthOfWorkBlocks = maxLengthOfWorkBlocks;
    }

    public int getMinLengthOfDayOffBlocks() {
        return minLengthOfDayOffBlocks;
    }

    public void setMinLengthOfDayOffBlocks(int minLengthOfDayOffBlocks) {
        this.minLengthOfDayOffBlocks = minLengthOfDayOffBlocks;
    }

    public int getMaxLengthOfDayOffBlocks() {
        return maxLengthOfDayOffBlocks;
    }

    public void setMaxLengthOfDayOffBlocks(int maxLengthOfDayOffBlocks) {
        this.maxLengthOfDayOffBlocks = maxLengthOfDayOffBlocks;
    }

    

    public int[][] getNotAllowedSequences2() {
        return notAllowedSequences2;
    }

    public void setNotAllowedSequences2(int[][] notAllowedSequences2) {
        this.notAllowedSequences2 = notAllowedSequences2;
    }

    public int[][] getNotAllowedSequences3() {
        return notAllowedSequences3;
    }

    public void setNotAllowedSequences3(int[][] notAllowedSequences3) {
        this.notAllowedSequences3 = notAllowedSequences3;
    }


    public void printMembers() throws IllegalArgumentException, IllegalAccessException{
        
        for (Field field : this.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            String name = field.getName();
            Object value = field.get(this);
            
            System.out.printf("Field name: %s, Field value: %s%n", name, value);
        }
        
        
        //now the arrays
        System.out.println("temporal reqs:");
        System.out.println(Arrays.deepToString(this.temporalRequirementsMatrix));
        
        //now the arrays
        System.out.println("names:");
        System.out.println(Arrays.toString(this.names));
        
        //now the arrays
        System.out.println("minLengthOfBlocks:");
        System.out.println(Arrays.toString(this.minLengthOfBlocks));
        
        //now the arrays
        System.out.println("maxLengthOfBlocks:");
        System.out.println(Arrays.toString(this.maxLengthOfBlocks));
        
        //now the arrays
        System.out.println("notAllowedSequences2:");
        System.out.println(Arrays.deepToString(this.notAllowedSequences2));
        
        //now the arrays
        System.out.println("notAllowedSequences3:");
        System.out.println(Arrays.deepToString(this.notAllowedSequences3));
        
        
    }

    public SequencesConstraint getSeqConstraint() {
        return seqConstraint;
    }

    public void setSeqConstraint(SequencesConstraint seqConstraint) {
        this.seqConstraint = seqConstraint;
    }

    public BlockLengthConstraint getBlockConstraint() {
        return blockConstraint;
    }

    public void setBlockConstraint(BlockLengthConstraint blockConstraint) {
        this.blockConstraint = blockConstraint;
    }

    public int getNrOfNotAllowedSequences2() {
        return nrOfNotAllowedSequences2;
    }

    public void setNrOfNotAllowedSequences2(int nrOfNotAllowedSequences2) {
        this.nrOfNotAllowedSequences2 = nrOfNotAllowedSequences2;
    }

    public int getNrOfNotAllowedSequences3() {
        return nrOfNotAllowedSequences3;
    }

    public void setNrOfNotAllowedSequences3(int nrOfNotAllowedSequences3) {
        this.nrOfNotAllowedSequences3 = nrOfNotAllowedSequences3;
    }

    public int getkOpt() {
        return kOpt;
    }

    public void setkOpt(int kOpt) {
        this.kOpt = kOpt;
    }

    public float getStochasticHillClimbingT() {
        return stochasticHillClimbingT;
    }

    public void setStochasticHillClimbingT(float stochasticHillClimbingT) {
        this.stochasticHillClimbingT = stochasticHillClimbingT;
    }

    public float getSimulatedAnnealingR() {
        return simulatedAnnealingR;
    }

    public void setSimulatedAnnealingR(float simulatedAnnealingR) {
        this.simulatedAnnealingR = simulatedAnnealingR;
    }

    public float getStartStochasticHillClimbingT() {
        return startStochasticHillClimbingT;
    }

    public void setStartStochasticHillClimbingT(float startStochasticHillClimbingT) {
        this.startStochasticHillClimbingT = startStochasticHillClimbingT;
    }

    public int getStopLimit() {
        return stopLimit;
    }

    public void setStopLimit(int stopLimit) {
        this.stopLimit = stopLimit;
    }

    void resetT() {
        this.stochasticHillClimbingT = this.startStochasticHillClimbingT;    
    }

    void decreaseT() {
        this.stochasticHillClimbingT = stochasticHillClimbingT*(1-this.simulatedAnnealingR);
    }

    boolean decreaseTEveryTime() {
        return this.decreaseTEveryTime;
    }

    public boolean isDecreaseTEveryTime() {
        return decreaseTEveryTime;
    }

    public void setDecreaseTEveryTime(boolean decreaseTEveryTime) {
        this.decreaseTEveryTime = decreaseTEveryTime;
    }
    
    

    
    
}
